package com.xingchi.tornado.storage.core.service.impl;

import com.xingchi.tornado.basic.ErrorCode;
import com.xingchi.tornado.exception.BizException;
import com.xingchi.tornado.security.context.RequestContext;
import com.xingchi.tornado.storage.core.FileStorageService;
import com.xingchi.tornado.storage.core.converter.DownloadFileHolder;
import com.xingchi.tornado.storage.core.model.FileUploadWrapper;
import com.xingchi.tornado.storage.core.model.dto.FileDTO;
import com.xingchi.tornado.storage.core.model.dto.FileUserRelDTO;
import com.xingchi.tornado.storage.core.model.dto.FileUserRelQueryDTO;
import com.xingchi.tornado.storage.core.service.FileService;
import com.xingchi.tornado.storage.core.service.FileUploadService;
import com.xingchi.tornado.storage.core.service.FileUserRelService;
import com.xingchi.tornado.storage.core.utils.FileTypeChecker;
import com.xingchi.tornado.storage.core.utils.FileUtils;
import com.xingchi.tornado.utils.DigestUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Objects;

/**
 * 文件上传服务实现
 *
 * @author xingchi
 * @date 2024/5/4 21:37
 */
@Slf4j
@Service
public class FileUploadServiceImpl implements FileUploadService {

    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private FileService fileService;
    @Autowired
    private FileUserRelService fileUserRelService;


    /**
     * 上传文件
     *
     * @param fileUploadWrapper 文件上传上下文
     * @return 用户文件关联信息
     */
    @Override
    public FileUserRelDTO uploadFile(FileUploadWrapper fileUploadWrapper) {

        BizException.throwIf(fileUploadWrapper.isEmpty(), ErrorCode.FILE_UPLOAD_FAILED, "文件上传失败，找不到文件内容");

        if (fileUploadWrapper.getUserId() == null) {
            Long userId = RequestContext.getUserId();
            fileUploadWrapper.setUserId(Objects.requireNonNullElse(userId, -1L));
        }


        Path filePath = null;
        try {

            // 先将文件写入到本地
            String suffix = FilenameUtils.getExtension(fileUploadWrapper.getFileName());
            filePath = Files.createTempFile("tornado.file.", "." + suffix);
            // 执行写入
            long size;
            if (fileUploadWrapper.getBytes() != null) {
                size = fileUploadWrapper.getBytes().length;
                Files.copy(new ByteArrayInputStream(fileUploadWrapper.getBytes()), filePath, StandardCopyOption.REPLACE_EXISTING);
            } else {
                size = Files.copy(fileUploadWrapper.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
            }

            // 再查询文件是否存在
            String sha256 = DigestUtils.sha256(filePath.toFile());
            FileDTO fileDTO = fileService.selectBySha256(sha256);

            if (fileDTO == null) {
                // 新文件，执行上传操作逻辑
                String fileName = fileUploadWrapper.getFileName();
                Integer type = FileTypeChecker.getFileTypeCode(fileName);
                fileDTO = FileDTO.builder()
                        .sha256(sha256)
                        .name(fileName)
                        .path(fileStorageService.uploadFile(filePath))
                        .size(size)
                        .type(type)
                        .suffix(suffix)
                        .build();
                fileService.insert(fileDTO);
            }

            // 构建用户文件联系直接插入
            FileUserRelDTO fileUserRelDTO = FileUserRelDTO.builder()
                    .fileId(fileDTO.getId())
                    .userId(fileUploadWrapper.getUserId())
                    .name(fileDTO.getName())
                    .path(fileDTO.getPath())
                    .size(fileDTO.getSize())
                    .suffix(fileDTO.getSuffix())
                    .type(fileDTO.getType())
                    .sha256(fileDTO.getSha256())
                    .build();
            fileUserRelService.insert(fileUserRelDTO);
            return fileUserRelDTO;
        } catch (IOException e) {
            log.warn("文件上传失败，原因：{}", e.getMessage(), e);
            BizException.throwOf(ErrorCode.FILE_UPLOAD_FAILED, e.getMessage());
        } finally {
            if (filePath != null && Files.exists(filePath)) {
                try {
                    Files.delete(filePath);
                } catch (IOException e) {
                    log.warn("文件删除失败，filePath: {}", filePath);
                }
            }
            IOUtils.closeQuietly(fileUploadWrapper.getInputStream());
        }
        return null;
    }

    @Override
    public String getUrl(Long id) {
        FileUserRelDTO fileUserRelDTO = fileUserRelService.selectById(id);
        BizException.throwIf(Objects.isNull(fileUserRelDTO), ErrorCode.FILE_NOT_FOUND, "文件不存在");
        return fileStorageService.getUrl(fileUserRelDTO.getPath());
    }

    @Override
    public String getUrl(String path) {
        return fileStorageService.getUrl(path);
    }

    /**
     * 下载文件
     *
     * @param id 文件id
     */
    @Override
    public void download(Long id) {
        DownloadFileHolder inputStream = getInputStream(id);
        FileUtils.download(inputStream.getFileName(), inputStream.getInputStream());
    }

    /**
     * 下载文件
     *
     * @param path 文件路径
     */
    @Override
    public void download(String path) {
        DownloadFileHolder inputStream = getInputStream(path);
        FileUtils.download(inputStream.getFileName(), inputStream.getInputStream());
    }

    /**
     * 获取文件流
     *
     * @param id 文件id
     * @return 文件信息
     */
    @Override
    public DownloadFileHolder getInputStream(Long id) {

        FileUserRelDTO fileUserRelDTO = fileUserRelService.selectById(id);
        BizException.throwIf(Objects.isNull(fileUserRelDTO), ErrorCode.FILE_NOT_FOUND, "文件不存在");

        String path = fileUserRelDTO.getPath();
        InputStream inputStream = fileStorageService.getInputStream(path);

        return new DownloadFileHolder(fileUserRelDTO.getName(), fileUserRelDTO.getSha256(), inputStream);
    }

    /**
     * 获取文件流
     *
     * @param path 文件路径
     * @return 文件信息
     */
    @Override
    public DownloadFileHolder getInputStream(String path) {

        FileUserRelDTO fileUserRelDTO = fileUserRelService.selectOne(FileUserRelQueryDTO.builder().path(path).build());
        BizException.throwIf(Objects.isNull(fileUserRelDTO), ErrorCode.FILE_NOT_FOUND, "文件不存在");

        InputStream inputStream = fileStorageService.getInputStream(path);

        return new DownloadFileHolder(fileUserRelDTO.getName(), fileUserRelDTO.getSha256(), inputStream);
    }

}
